﻿using AngleSharp.Dom;
using AngleSharp.Html.Dom;
using AngleSharp.Html.Parser;
using ShareHelper;
using System;
using System.Collections.Generic;
using System.Linq;

namespace 小说爬取
{
    public abstract class HtmlParse : ChapterParse
    {
        /// <summary>
        /// 可以循环使用的 Html解析器
        /// </summary>
        private static HtmlParser HtmlParser { get; }

        static HtmlParse()
        {
            HtmlParser = new HtmlParser();
        }

        /// <summary>
        /// 小说名称选择器
        /// </summary>
        private NameSelector NameSelector { get; }

        /// <summary>
        /// 章节选择器
        /// </summary>
        private ChapterSelector ChapterSelector { get; }

        /// <summary>
        /// 小说章节内容 选择器
        /// </summary>
        private string TextSelector { get; }

        /// <summary>
        /// /创建小说解析类
        /// </summary>
        /// <param name="naemSelector">小说名Css路径</param>
        public HtmlParse(Uri uri, NameSelector naemSelector, ChapterSelector chapterSelector, string Selector, string filtration = null) : base(uri, filtration)
        {
            NameSelector = naemSelector;
            ChapterSelector = chapterSelector;
            TextSelector = Selector;
        }

        /// <summary>
        ///小说名称
        /// </summary>

        private string ChapterName;

        /// <summary>
        /// 小说名称
        /// </summary>
        public override string Name => ChapterName;

        public override string GetChapter(Chapter chapter)
        {
            try
            {
                if (Get(chapter.Href, out IHtmlDocument html, out string err))
                {
                    chapter.Text = html.QuerySelector(TextSelector).TextContent;
                }
                else
                    return err;
            }
            catch (Exception e)
            {
                return e.GetErr();
            }

            return string.Empty;
        }

        /// <summary>
        /// 小说章节目录解析
        /// </summary>
        /// <param name="err"></param>
        /// <returns></returns>
        public override bool Parse(out string err)
        {
            if (GetName(Uri, out err))
            {
                var data = GetChapterAll();
                if (data.Any())
                {
                    var chapters = new List<Chapter>();
                    int Id = 0;
                    foreach (var item in data)
                    {
                        if (ChapterParse(item, out Chapter chapter))
                        {
                            chapter.Id = Id++;
                            chapters.Add(chapter);
                        }
                        else
                        {
                            err = $"章节:{item.OuterHtml},解析失败";
                            chapters.Clear();
                            break;
                        }
                    }
                    Chapters = chapters;
                }
                else
                {
                    err = "小说章节列表解析失败";
                }
            }
            return Chapters.Count > 0;
        }

        /// <summary>
        /// 章节解析
        /// </summary>
        /// <param name="element"></param>
        protected virtual bool ChapterParse(IElement element, out Chapter chapter)
        {
            chapter = new Chapter
            {
                Name = element?.TextContent
            };
            var href = element?.Attributes["href"]?.Value;
            if (!string.IsNullOrEmpty(href))
            {
                var GetUri = ChapterSelector.GetUri;
                if (GetUri != null)
                {
                    if (GetUri.Invoke(Uri, href, out var uri))
                    {
                        chapter.Href = uri;
                        return !string.IsNullOrEmpty(chapter.Name);
                    }
                }
            }
            return false;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="html"></param>
        /// <param name="err"></param>
        /// <returns></returns>
        protected static bool Get(Uri uri, out IHtmlDocument html, out string err)
        {
            if (Get(uri, out string text, out err))
                html = ParseText(text);
            else
                html = null;
            return !string.IsNullOrEmpty(text);
        }

        /// <summary>
        /// 网页内容解析到  HtmlDocument
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        protected static IHtmlDocument ParseText(string text)
        {
            return HtmlParser.ParseDocument(text);
        }

        /// <summary>
        /// 网页Html 对象
        /// </summary>
        private IHtmlDocument Document;

        /// <summary>
        /// 获取小说名称
        /// </summary>
        /// <param name="url">小说地址</param>
        /// <param name="err">错误原因</param>
        /// <param name="selectors">CSS选择器路径</param>
        /// <param name="Attribute">属性可疑为空 为空取内部文本</param>
        /// <returns>小说名称获取成功 返回true  获取失败 返回 false</returns>
        private bool GetName(Uri uri, out string err)
        {
            if (Get(uri, out string text, out err))
            {
                Document = ParseText(text);
                var element = Document.QuerySelector(NameSelector.Selectors);
                if (string.IsNullOrEmpty(NameSelector.Attribute))
                    ChapterName = element?.TextContent;
                else
                    ChapterName = element?.Attributes[NameSelector.Attribute]?.Value;
                if (string.IsNullOrEmpty(ChapterName))
                    err = "小说名称获取失败";
            }
            return !string.IsNullOrEmpty(ChapterName);
        }

        /// <summary>
        /// 获取章节名称 地址 集合
        /// </summary>
        /// <param name="Document"></param>
        /// <param name="selectors"></param>
        /// <param name="list"></param>
        /// <returns></returns>
        private IEnumerable<IElement> GetChapterAll()
        {
            IEnumerable<IElement> data = null;
            if (!string.IsNullOrEmpty(ChapterName))
            {
                IParentNode Chapter = Document;
                if (!string.IsNullOrEmpty(ChapterSelector.ListSelectors))
                    Chapter = Document.QuerySelector(ChapterSelector.ListSelectors);
                data = Chapter?.QuerySelectorAll(ChapterSelector.Selectors);
            }
            return data ?? new List<IElement>();
        }
    }

    /// <summary>
    /// 小说名称解析路径
    /// </summary>
    public class NameSelector
    {
        /// <summary>
        /// 小说名称解析规则
        /// </summary>
        /// <param name="selectors">Css路径</param>
        /// <param name="attribute">所在属性 为空取内部文本</param>
        public NameSelector(string selectors, string attribute = null)
        {
            Selectors = selectors;
            Attribute = attribute;
        }

        /// <summary>
        /// css 路径
        /// </summary>
        public string Selectors { get; }

        /// <summary>
        /// 名称所在属性
        /// </summary>
        public string Attribute { get; }
    }

    /// <summary>
    /// 小说章节解析规则
    /// </summary>
    public class ChapterSelector
    {
        /// <summary>
        /// 小说名称解析规则
        /// </summary>
        /// <param name="selectors">单章节 css 路径</param>
        /// <param name="getUrl">拼接Url的 函数</param>
        /// <param name="listSelectors">章节集合所在路径</param>
        public ChapterSelector(string selectors, DelegateGetUrl getUrl = null, string listSelectors = null)
        {
            Selectors = selectors;
            ListSelectors = listSelectors;
            GetUri = getUrl;
        }

        /// <summary>
        /// 章节集合所在的路径
        /// </summary>
        public string ListSelectors { get; }

        /// <summary>
        /// 单独一章 集合路径 下所在的路径
        /// </summary>
        public string Selectors { get; }

        /// <summary>
        /// Url 拼接委托
        /// </summary>
        /// <param name="baseUri"></param>
        /// <param name="relativeUri"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public delegate bool DelegateGetUrl(Uri baseUri, string relativeUri, out Uri result);

        /// <summary>
        /// Url拼接
        /// </summary>
        public DelegateGetUrl GetUri { get; }
    }
}